# -*- coding: utf-8 -*-
"""
    Relational Database Proxy.

    :TODO ONLY MySQL is partly supported.

    :copyright: (C) 2015 by Yu Jianjian
    :license: iqiyi.com
"""

import logging
import collections
import time
import pymysql


LOG = logging.getLogger(__name__)

Status = collections.namedtuple('DB_Status', 'status message')


class MysqlLiteral(object):

    def __init__(self, param):
        self.param = param

    def __str__(self):
        return self.param

    def __repr__(self):
        return '<%s: `%s`>' % (self.__class__.__name__, self.param)


now = (lambda: time.strftime('%Y-%m-%d %H:%M:%S'))


class MysqlProxy(object):

    def __init__(self, host, db, user, password=None, port=3306):
        auth = dict(host=host, user=user,
                    port=port, db=db,
                    charset='utf8', cursorclass=pymysql.cursors.DictCursor)
        if password:
            auth.update(passwd=password)

        self.auth = auth
        self.connection = pymysql.connect(**auth)
        self.cursor = self.connection.cursor()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()
        if exc_type:
            LOG.error('%s:%s', exc_type, exc_val)

    def ping(self):
        """ Keep connection always alive.
        """
        try:
            self.connection.ping()
            return True
        except Exception as err:
            tid = self.connection.thread_id()
            LOG.error('Mysql %s is miss connected: %s', tid, err)
            return False

    def sql(self, sql):
        action_type = sql.split(' ')[0].lower()

        try:
            if action_type in ('truncate', 'delete', 'update', 'insert'):
                affected_rows = self.cursor.execute(sql)
                self.connection.commit()
                status, message = 0, affected_rows
            elif action_type == 'select':
                self.cursor.execute(sql)
                self.connection.commit()
                status, message = 0, list(self.cursor.fetchall())
            else:
                status, message = 1, 'operation not supported'
        except Exception as err:
            LOG.error('%s => `%s`', err, sql)
            status, message = 1, '%s' % err.args[1]

        return Status(status, message)

    def get(self, sql):
        try:
            self.cursor.execute(sql)
            self.connection.commit()
            raw_records = self.cursor.fetchall()
            status, message = 0, list(raw_records)
        except Exception as err:
            LOG.error('%s => `%s`', err, sql)
            status, message = 1, err.args[1]

        return Status(status, message)

    def one(self, sql):
        try:
            self.cursor.execute(sql)

            # FIXME: New record after existed persistent connection was hidden at times.
            # Hopefully, commit could flush that.
            self.connection.commit()

            record = self.cursor.fetchone()
            status, message = 0, record
        except Exception as err:
            LOG.error('%s => %s', err, sql)
            status, message = 1, err.args[1]
        return Status(status, message)

    def mod(self, sql):
        try:
            r = self.cursor.execute(sql)
            self.connection.commit()
            status, message = 0, r
            LOG.debug("Mod_SQL is:{0}, affected row: {1}".format(sql, message))
        except Exception as err:
            self.connection.rollback()
            LOG.error('Raise Exception while modifying `%s`: %s', sql, err)
            status, message = 1, err.args[1]

        return Status(status, message)

    def add(self, sql):
        try:
            self.cursor.execute(sql)
            self.connection.commit()

            self.cursor.execute('SELECT LAST_INSERT_ID() AS last_id')
            last_id = self.cursor.fetchone().get('last_id')
            status, message = 0, last_id
            LOG.debug("sql => `{0}`".format(sql))
        except Exception as err:
            self.connection.rollback()
            LOG.error('%s => `%s`', err, sql)
            status, message = 1, err.args[1]

        return Status(status, message)

    def remove(self, sql):
        try:
            affect_rows = self.cursor.execute(sql)
            self.connection.commit()
            status, message = 0, affect_rows

        except Exception as err:
            self.connection.rollback()
            LOG.error('%s => `%s`', err, sql)
            status, message = 1, err.args[1]

        return Status(status, message)

    def join(self, sql):
        try:
            self.cursor.execute(sql)
            self.connection.commit()
            raw_records = self.cursor.fetchall()
            status, message = 0, list(raw_records)
        except Exception as err:
            self.connection.rollback()
            LOG.error('%s => `%s`', err, sql)
            status, message = 1, err.args[1]

        return Status(status, message)

    def close(self):
        try:
            if self.cursor:
                self.cursor.close()

            if self.connection:
                self.connection.close()
        except Exception as err:
            LOG.error('close db connection failed: %s', err)
